package com.vast.metrix.recorder.bustraffic.viewers;

import java.awt.Color;
import java.awt.Font;

import org.jfree.chart.annotations.XYTitleAnnotation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.AbstractRenderer;
import org.jfree.chart.renderer.xy.DefaultXYItemRenderer;
import org.jfree.chart.renderer.xy.XYAreaRenderer;
import org.jfree.chart.renderer.xy.XYAreaRenderer2;
import org.jfree.chart.renderer.xy.XYStepAreaRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;

public class BusTrafficPlotter {
	private String     busName;
	private XYPlot     subPlot;
	private NumberAxis rangeAxisLeftside;
	private NumberAxis rangeAxisRightside;
	private NumberAxis rangeAxisRightside2;
	private boolean    isMaximumItemAge_ms = true;
	private boolean    isAutoRange         = false;
	private int        maximumItemAge_ms   = 500;
	private int        samplingRate_ms = 10;
	private double     upperRangeAxisLeftside;
	private double     upperRangeAxisRightside;
	private double     upperRangeAxisRightside2;
	private TimeSeries transferByteSeries;
	private TimeSeries activitySeries;
	private TimeSeries latencySeries;
	private TimeSeries utilizationSeries;
	private TimeSeriesCollection transferByteDataset;
	private TimeSeriesCollection activityDataset;
	private TimeSeriesCollection latencyAndUtilizationDataset;
	
	public BusTrafficPlotter(String busName, double upperRangeAxisLeftside, double upperRangeAxisRightside, double upperRangeAxisRightside2, int samplingRate_ms) {
		createChart(busName, upperRangeAxisLeftside, upperRangeAxisRightside, upperRangeAxisRightside2, samplingRate_ms, maximumItemAge_ms, this.isMaximumItemAge_ms, this.isAutoRange);
	}
	
	public BusTrafficPlotter(String busName, double upperRangeAxisLeftside, double upperRangeAxisRightside, double upperRangeAxisRightside2, int samplingRate_ms, int maximumItemAge_ms) {
		createChart(busName, upperRangeAxisLeftside, upperRangeAxisRightside, upperRangeAxisRightside2, samplingRate_ms, maximumItemAge_ms, this.isMaximumItemAge_ms, this.isAutoRange);
	}
	
	public BusTrafficPlotter(String busName, double upperRangeAxisLeftside, double upperRangeAxisRightside, double upperRangeAxisRightside2, int samplingRate_ms, int maximumItemAge_ms, boolean isMaximumItemAge_ms, boolean isAutoRange) {
		createChart(busName, upperRangeAxisLeftside, upperRangeAxisRightside, upperRangeAxisRightside2, samplingRate_ms, maximumItemAge_ms, isMaximumItemAge_ms, isAutoRange);
	}
	
	public void createChart(String busName, double upperRangeAxisLeftside, double upperRangeAxisRightside, double upperRangeAxisRightside2, int samplingRate_ms, int maximumItemAge_ms, boolean isMaximumItemAge_ms, boolean isAutoRange) {
		this.isMaximumItemAge_ms      = isMaximumItemAge_ms;
		this.isAutoRange              = isAutoRange;
		this.busName                  = busName;
		this.samplingRate_ms          = samplingRate_ms;
		this.maximumItemAge_ms        = maximumItemAge_ms;
		this.upperRangeAxisLeftside   = upperRangeAxisLeftside;
		this.upperRangeAxisRightside  = upperRangeAxisRightside;
		this.upperRangeAxisRightside2 = upperRangeAxisRightside2;
		this.transferByteSeries      = new TimeSeries("TransferByte");
		this.latencySeries           = new TimeSeries("Latency");
		this.utilizationSeries       = new TimeSeries("Utilization");
		this.activitySeries          = new TimeSeries("Activity");
		
		if(this.isMaximumItemAge_ms) {
			this.transferByteSeries.setMaximumItemAge(this.maximumItemAge_ms);
			this.activitySeries.setMaximumItemAge(this.maximumItemAge_ms);
			this.latencySeries.setMaximumItemAge(this.maximumItemAge_ms);
			this.utilizationSeries.setMaximumItemAge(this.maximumItemAge_ms);
		}
	
		this.transferByteDataset          = new TimeSeriesCollection(this.transferByteSeries);
		this.activityDataset              = new TimeSeriesCollection(this.activitySeries);
		this.latencyAndUtilizationDataset = new TimeSeriesCollection(this.latencySeries);
		this.latencyAndUtilizationDataset.addSeries(this.utilizationSeries);
		
		/*
		 *  Define rangeAxis(Y-Axis) at left side
		 */
		this.rangeAxisLeftside = new NumberAxis("[bytes/" + this.samplingRate_ms + "ms]");
		this.rangeAxisLeftside.setTickLabelFont(new Font("SansSerif", Font.PLAIN, 9));
		this.rangeAxisLeftside.setLabelFont(new Font("SansSerif", Font.PLAIN, 9));
		this.rangeAxisLeftside.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
		if(this.isAutoRange) {
			this.rangeAxisLeftside.setAutoRange(true);
		} else {
			this.rangeAxisLeftside.setAutoRange(false);
			this.rangeAxisLeftside.setRange(0, this.upperRangeAxisLeftside);
		}
		this.rangeAxisLeftside.setAutoRangeIncludesZero(false);

		/*
		 * Define sub plot
		 */
		/* Transfer Byte */
		DefaultXYItemRenderer rendererTransfer = new DefaultXYItemRenderer();
		rendererTransfer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
		this.subPlot = new XYPlot(this.transferByteDataset, null, rangeAxisLeftside,	rendererTransfer);   
		this.subPlot.setBackgroundPaint(Color.black);
		this.subPlot.setDomainGridlinePaint(Color.green);
		this.subPlot.setRangeGridlinePaint(Color.green);
		
		/* Activity */
		this.subPlot.setDataset(1, this.activityDataset);
		this.rangeAxisRightside = new NumberAxis("[times/" + this.samplingRate_ms + "ms]");
		this.subPlot.setRenderer(1, new DefaultXYItemRenderer());
		this.subPlot.setRangeAxis(1, this.rangeAxisRightside);
		this.subPlot.mapDatasetToRangeAxis(1, 1);
		
		/* Latency and Utilization */
		this.subPlot.setDataset(2, this.latencyAndUtilizationDataset);
		this.rangeAxisRightside2 = new NumberAxis("[ticks/" + this.samplingRate_ms + "ms]");
		this.subPlot.setRenderer(2, new XYStepAreaRenderer(XYStepAreaRenderer.AREA));
		//this.subPlot.setRenderer(2, new XYAreaRenderer2());
		this.subPlot.setRangeAxis(2, this.rangeAxisRightside2);
		this.subPlot.mapDatasetToRangeAxis(2, 2);
			
		/* Range Axis*/		
		if(this.isAutoRange) {
			this.rangeAxisRightside.setAutoRange(true);
		} else {
			this.rangeAxisRightside.setAutoRange(false);
			this.rangeAxisRightside.setRange(0, this.upperRangeAxisRightside);
		}
		rangeAxisRightside.setAutoRangeIncludesZero(false);
		
		if(this.isAutoRange) {
			this.rangeAxisRightside2.setAutoRange(true);
		} else {
			this.rangeAxisRightside2.setAutoRange(false);
			this.rangeAxisRightside2.setRange(0, this.upperRangeAxisRightside2);
		}
		rangeAxisRightside2.setAutoRangeIncludesZero(false);
		
		/* Renderer */
		AbstractRenderer r1 = (AbstractRenderer) subPlot.getRenderer(0);
		AbstractRenderer r2 = (AbstractRenderer) subPlot.getRenderer(1);
		AbstractRenderer r3 = (AbstractRenderer) subPlot.getRenderer(2);
		
		r1.setSeriesPaint(0, Color.red, false);
		((DefaultXYItemRenderer)r1).setSeriesShapesVisible(0, false);
		
		r2.setSeriesPaint(0, Color.magenta, false);
		((DefaultXYItemRenderer)r2).setSeriesShapesVisible(0, false);
		
		r3.setSeriesPaint(0, Color.yellow, false);
		r3.setSeriesPaint(1, Color.blue, false);
		
		/* Legend inside the graph */
		LegendTitle lt = new LegendTitle(subPlot);
        lt.setItemFont(new Font("SansSerif", Font.PLAIN, 9));
        lt.setBackgroundPaint(new Color(225, 225, 255, 126));
        lt.setFrame(new BlockBorder(Color.lightGray));
        lt.setPosition(RectangleEdge.BOTTOM);
        
        XYTitleAnnotation ta = new XYTitleAnnotation(0.98, 0.02, lt, RectangleAnchor.BOTTOM_RIGHT);
        ta.setMaxWidth(0.9);
        subPlot.addAnnotation(ta);
        
        TextTitle graphTitle = new TextTitle(this.busName);
        graphTitle.setFont(new Font("SansSerif", Font.PLAIN, 9));
        graphTitle.setPaint(Color.white);
        //graphTitle.setBackgroundPaint(new Color(255, 255, 255, 255));
        //graphTitle.setFrame(new BlockBorder(Color.blue));
        graphTitle.setPosition(RectangleEdge.BOTTOM);
        XYTitleAnnotation graphTitleAnnotation = new XYTitleAnnotation(0.02, 0.98, graphTitle, RectangleAnchor.TOP_LEFT);
        graphTitleAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(graphTitleAnnotation);
        
        this.subPlot.setForegroundAlpha(0.6f);
	}
	
	public XYPlot getSubPlot() {
		return this.subPlot;
	}
	
	public TimeSeries getTransferByteSeries() {
		return this.transferByteSeries;
	}
	
	public TimeSeries getLatencySeries() {
		return this.latencySeries;
	}
	
	public TimeSeries getUtilizationSeries() {
		return this.utilizationSeries;
	}
	
	public TimeSeries getActivitySeries() {
		return this.activitySeries;
	}
	
	public void fireSeriesChanged() {
		transferByteSeries.fireSeriesChanged();
	}
	
	public void clearTimeSeries() {
		this.transferByteSeries.clear();
		this.activitySeries.clear();
		this.latencySeries.clear();
		this.utilizationSeries.clear();
	}
	
	public void clearTimeSeries(int start, int end) {
		this.transferByteSeries.delete(start, end);
		this.activitySeries.delete(start, end);
		this.latencySeries.delete(start, end);
		this.utilizationSeries.delete(start, end);
	}
	
	public void UpdateMaximumItemAge(int maximumItemAge_ms) {
		this.maximumItemAge_ms = maximumItemAge_ms;
		this.transferByteSeries.setMaximumItemAge(this.maximumItemAge_ms);
		this.activitySeries.setMaximumItemAge(this.maximumItemAge_ms);
		this.latencySeries.setMaximumItemAge(this.maximumItemAge_ms);
		this.utilizationSeries.setMaximumItemAge(this.maximumItemAge_ms);
	}
	
	public void addBusTraffic(final long currentTime_ms, final double transferByte, final double activity, final double latency, final double utilization) {
		this.transferByteSeries.add(new FixedMillisecond(currentTime_ms), transferByte, false);
		this.activitySeries.add(new FixedMillisecond(currentTime_ms), activity, false);
		this.latencySeries.add(new FixedMillisecond(currentTime_ms), latency, false);
		this.utilizationSeries.add(new FixedMillisecond(currentTime_ms), latency + utilization, false);	
	}
	
	public void addBusTraffic(final long currentTime_ms, final double transferByte, final double activity, final double latency, final double utilization, boolean isSynchronized) {
		this.transferByteSeries.add(new FixedMillisecond(currentTime_ms), transferByte, isSynchronized);
		this.activitySeries.add(new FixedMillisecond(currentTime_ms), activity, isSynchronized);
		this.latencySeries.add(new FixedMillisecond(currentTime_ms), latency, isSynchronized);
		this.utilizationSeries.add(new FixedMillisecond(currentTime_ms), latency + utilization, isSynchronized);	
	}
	
	public void UpdateUpperValueOfRangeAxis(double upperRangeAxisLeftside, double upperRangeAxisRightside, double upperRangeAxisRightside2) {
		if (this.upperRangeAxisLeftside < upperRangeAxisLeftside) {
			this.upperRangeAxisLeftside  = upperRangeAxisLeftside;
			double maxRangeAxisLeftside  = this.upperRangeAxisLeftside;
			this.rangeAxisLeftside.setRange(0, maxRangeAxisLeftside);
		}
		if (this.upperRangeAxisRightside < upperRangeAxisRightside) {
			this.upperRangeAxisRightside = upperRangeAxisRightside;	
			double maxRangeAxisRightside = this.upperRangeAxisRightside;
			this.rangeAxisRightside.setRange(0, maxRangeAxisRightside);
		}
		if (this.upperRangeAxisRightside2 < upperRangeAxisRightside2) {
			this.upperRangeAxisRightside2 = upperRangeAxisRightside2;
			double maxRangeAxisRightside2 = this.upperRangeAxisRightside2;
			this.rangeAxisRightside2.setRange(0, maxRangeAxisRightside2);
		}
	}
	
	public void setAutoRange(boolean isAutoRange) {
		this.isAutoRange  = isAutoRange;
		if(this.isAutoRange) {
			this.rangeAxisLeftside.setAutoRange(true);
			
			this.rangeAxisRightside.setAutoRange(true);
			
			this.rangeAxisRightside2.setAutoRange(true);
		} else {
			this.rangeAxisLeftside.setAutoRange(false);
			this.rangeAxisLeftside.setRange(0, this.upperRangeAxisLeftside);
			
			this.rangeAxisRightside.setAutoRange(false);
			this.rangeAxisRightside.setRange(0, this.upperRangeAxisRightside);
			
			this.rangeAxisRightside2.setAutoRange(false);
			this.rangeAxisRightside2.setRange(0, this.upperRangeAxisRightside2);
		}
	}
  
}
